package de.tu_darmstadt.informatik.tk.lernsystem.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdException;
import org.expressme.openid.OpenIdManager;
import org.json.simple.JSONObject;
import org.json.simple.JSONStreamAware;

import de.tu_darmstadt.informatik.tk.lernsystem.concepts.User;
import de.tu_darmstadt.informatik.tk.lernsystem.dao.UserDao;
import de.tu_darmstadt.informatik.tk.lernsystem.util.Md5;

public class OpenIdServlet extends HttpServlet {

	static final long ONE_HOUR = 3600000L;
	static final long TWO_HOUR = ONE_HOUR * 2L;
	static final String ATTR_MAC = "openid_mac";
	static final String ATTR_ALIAS = "openid_alias";

	private OpenIdManager manager;

	@Override
	public void init() throws ServletException {
		super.init();
		manager = new OpenIdManager();
		manager.setRealm("http://localhost:8080");
		manager.setReturnTo("http://localhost:8080/KollaborativesLernsystem/openid");
	}

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		HttpSession httpSession = request.getSession();
		
		
		if (request.getParameter("logout")!=null){
			logout(httpSession);
			return;
		}
		byte[] mac = null;
		String alias = null;
		String firstName = null;
		String lastName = null;
		String userId = null;

		String op = request.getParameter("op");
		if (op != null && (op.equals("Google") || op.equals("Yahoo"))) {
			// redirect to Google or Yahoo sign on page:
			Endpoint endpoint = manager.lookupEndpoint(op);
			Association association = manager.lookupAssociation(endpoint);
			httpSession.setAttribute(ATTR_MAC, association.getRawMacKey());
			httpSession.setAttribute(ATTR_ALIAS, endpoint.getAlias());
			String url = manager.getAuthenticationUrl(endpoint, association);
			response.sendRedirect(url);
			return;
		} else {
			userId = (String) httpSession.getAttribute("userId");
			firstName = (String) httpSession.getAttribute("firstName");
			lastName = (String) httpSession.getAttribute("lastName");
			if (userId!=null){
				JSONObject userData = new JSONObject();
				userData.put("email", userId);
				userData.put("firstName",firstName);
				userData.put("lastName", lastName);
				response.getWriter().write(userData.toJSONString());
				return;
			}
			// Check the timelimit
			if (checkNonce(request.getParameter("openid.response_nonce"))) {
				
				// Try to get the authentication
				mac = (byte[]) httpSession.getAttribute(ATTR_MAC);
				alias = (String) httpSession.getAttribute(ATTR_ALIAS);
				Authentication authentication = manager.getAuthentication(
						request, mac, alias);
				// Save user data into Session
				User u1 = UserDao.getUserByUsername(authentication.getEmail());
				if (u1 == null) {
					u1 = UserDao.createUser();
					u1.setUsername(authentication.getEmail());
					u1.setCredentialToken(Md5.getHash(String.valueOf(System.currentTimeMillis())));
				}
				
				response.addCookie(new Cookie("token", u1.getCredentialToken()));
				httpSession.setAttribute("userId", authentication.getEmail());
				httpSession.setAttribute("firstName",
						authentication.getFirstname());
				httpSession.setAttribute("lastName",
						authentication.getLastname());
				response.sendRedirect("http://localhost:8080/KollaborativesLernsystem/");
				return;
			} else {
				redirectToAuthentication(httpSession, response);
				return;
			}
		}
	}

	private void redirectToAuthentication(HttpSession httpSession,
			HttpServletResponse response) throws IOException {
		response.setContentType("application/json");
		JSONObject json = new JSONObject();
		json.put("login", "true");
		response.getWriter().write(json.toJSONString());

	}

	
	boolean checkNonce(String nonce) {

		// check response_nonce to prevent replay-attack:
		if (nonce == null || nonce.length() < 20)
			return false;
		// make sure the time of server is correct:
		long nonceTime = getNonceTime(nonce);
		long diff = Math.abs(System.currentTimeMillis() - nonceTime);
		if (diff > ONE_HOUR)
			return false;
		if (!isNonceExist(nonce))
			storeNonce(nonce, nonceTime + TWO_HOUR);
		return true;
	}

	// simulate a database that store all nonce:
	private Set<String> nonceDb = new HashSet<String>();

	// check if nonce is exist in database:
	boolean isNonceExist(String nonce) {
		return nonceDb.contains(nonce);
	}

	// store nonce in database:
	void storeNonce(String nonce, long expires) {
		nonceDb.add(nonce);
	}

	long getNonceTime(String nonce) {
		try {
			return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(
					nonce.substring(0, 19) + "+0000").getTime();
		} catch (ParseException e) {
			throw new OpenIdException("Bad nonce time.");
		}
	}
	
	/*
	 * Removes the user data stored in the session
	 */
	void logout(HttpSession session){
		session.removeAttribute("userId");
		session.removeAttribute("firstName");
		session.removeAttribute("lastName");
	}

}
